﻿
#region Copyright & License
// --------------------------
// Copyright 2008 Daniel Gray
// --------------------------
// This file is part of Kale.
//
// Kale is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Kale is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Kale.  If not, see <http://www.gnu.org/licenses/>.
#endregion
#region File details
// --------------------------------------------------------
// Filename          : DiskStoreNodeProvider.cs
// Author            : Daniel Gray
// Created           : 25/04/2008
// --------------------------------------------------------
#endregion
            
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kale.Core.Configuration;
using System.IO;
using System.Security;

namespace Kale.Core
{
   public class DiskStoreNodeProvider : NodeProvider
   {
      private DirectoryInfo _pathInfo;

      /// <summary>
      /// Initializes a new instance of the <see cref="DiskStoreNodeProvider"/> class.
      /// </summary>
      /// <param name="name">The name.</param>
      /// <param name="pathInfo">The path info.</param>
      /// <param name="rootProvider">The root provider.</param>
      public DiskStoreNodeProvider(string name, DirectoryInfo pathInfo, INodeProvider rootProvider)
         : base(name, rootProvider)
      {
         _pathInfo = pathInfo;
      }

      /// <summary>
      /// Gets whether the specified path is contaied by this store.
      /// </summary>
      /// <param name="path">The path.</param>
      /// <returns><b>true</b> if the path is contained by this store, otherwise <b>false</b>.</returns>
      public bool PathIsContained(FileSystemInfo path)
      {
         return path.FullName.ToLower().StartsWith(_pathInfo.FullName.ToLower());
      }

      public override NodeSource SupportedNodeSourceType
      {
         get { return NodeSource.Disk; }
      }

      public override NodeSource? SupportedParentNodeSourceType
      {
         get { return NodeSource.Disk; }
      }

      public override IImageNode CreateNodeFromId(NodeId id)
      {

         FileSystemInfo nodePath = BuildPathForNodeId(id);

         if (nodePath is FileInfo)
         {
            return new ImageNode(id, NodeType.File, nodePath.FullName, nodePath.Name, RootProvider);
         }
         else if (nodePath is DirectoryInfo)
         {
            if (string.IsNullOrEmpty(id.Path))
            {
               return new ImageNode(id, NodeType.Folder, nodePath.FullName, id.Store, RootProvider);
            }
            else
            {
               return new ImageNode(id, NodeType.Folder, nodePath.FullName, nodePath.Name, RootProvider);
            }
         }
         else
         {
            throw new InvalidOperationException(string.Format("Invalid FileSystemInfo type for id {0}", id.ToString()));
         }
      }

      public override NodeId[] GetChildrenIds(NodeId id)
      {
         FileSystemInfo info = BuildPathForNodeId(id);

         if (info is FileInfo)
         {
            return new NodeId[0];
         }
         else if (info is DirectoryInfo)
         {
            DirectoryInfo dir = info as DirectoryInfo;
            List<NodeId> childIds = new List<NodeId>();

            foreach (DirectoryInfo subFolder in dir.GetDirectories())
            {
               if (!KaleConfigurationSection.Default.FolderExclusions.IsExcluded(subFolder.Name)) childIds.Add(CreateNodeId(subFolder));
            }

            foreach (FileInfo subFile in dir.GetFiles())
            {
               if (ImageFileHelper.SupportedImageExtensions.Contains(subFile.Extension.ToLower())) childIds.Add(CreateNodeId(subFile));
            }

            return childIds.ToArray();
         }
         else
         {
            throw new InvalidOperationException("Invalid FileSystemInfo type");
         }
      }

      protected override NodeId? GetParentId(NodeId id)
      {
         // figure out the path of the parent...
         if (string.IsNullOrEmpty(id.Path))
         {
            return NodeId.ParseFromIdString("Root");
         }
         else
         {
            string p = id.Path;
            int i = p.LastIndexOf("\\");

            if (i == -1) return new NodeId(NodeSource.Disk, id.Store, string.Empty);
            else
            {
               p = p.Substring(0, i);
               return new NodeId(NodeSource.Disk, id.Store, p);
            }
         }
      }

      private FileSystemInfo BuildPathForNodeId(NodeId id)
      {
         string path = string.Empty;

         if (string.IsNullOrEmpty(id.Path))
         {
            path = _pathInfo.FullName;
         }
         else
         {
            path = Path.Combine(_pathInfo.FullName, id.Path);
         }

         FileSystemInfo info = null;
         DirectoryInfo folder = new DirectoryInfo(path);

         if (folder.Exists)
         {
            info = folder;
         }
         else
         {
            FileInfo file = new FileInfo(path);
            if (file.Exists)
            {
               info = file;
            }
            else
            {
               return null;
            }
         }

         if (!PathIsContained(info))
         {
            throw new SecurityException(string.Format("Moved outside of top item path {0} with specified path {1} producing final path {2}", _pathInfo.FullName, id.Path, info.FullName));
         }

         return info;
      }

      private NodeId CreateNodeId(FileSystemInfo info)
      {
         string topItem = string.Empty;

         if (PathIsContained(info))
         {
            string relativePath = info.FullName.Replace(_pathInfo.FullName, string.Empty);

            if (relativePath.StartsWith("\\")) relativePath = relativePath.Substring(1);

            return new NodeId(NodeSource.Disk, Name, relativePath);
         }

         throw new InvalidOperationException("Can't build NodeId!");
      }
   }
}
